#include <plib.h>
#include <p32xxxx.h>
#include "flash.h"

#define SYS_FREQ 		(73728000L)     //73.728MHz
#define PB_DIV         		1
#define PRESCALE       		1


//FLASH instructions
unsigned char READ  =   0x03;   //read memory
unsigned char RDHS  =   0x0B;   //high-spped read instruction (follow by dummy byte)
unsigned char RDSR  =   0x05;   //read status register
unsigned char ERCP  =   0x60;   //erase full memory array
unsigned char WRBY  =   0x02;   //write one byte
unsigned char WRAI  =   0xAD;   //write using Auto-Address increment
unsigned char EWSR  =   0x50;   //enable write status register
unsigned char WRSR  =   0x01;   //write status register
unsigned char WREN  =   0x06;   //write enable
unsigned char WRDI  =   0x04;   //write disable
unsigned char EBSY  =   0x70;   //enable SO as output RY/BY# status during AAI programming
unsigned char DBSY  =   0x80;   //disable SO as output RY/BY# status during AAI programming
unsigned char AAIP  =   0xAD;   //Auto-Address Increment Program



//FLASH @ SPI1
void	ConfigFlash(void)
{

    FLS_SDI_TRIS = 1;
    FLS_SDO_TRIS = 0;
    FLS_WP_TRIS = 0;
    FLS_CE_TRIS = 0;
    FLS_SCK_TRIS = 0;

    FLS_CE_LAT = 1;     //initialize #CE on 1 (disable flash at start-up

    CloseSPI1();

    OpenSPI1(
            FRAME_ENABLE_OFF|
            ENABLE_SDO_PIN  |
            SPI_MODE8_ON    |
            SPI_SMP_ON      |
            SPI_CKE_ON      |
            SLAVE_ENABLE_OFF|
            CLK_POL_ACTIVE_HIGH  |
            MASTER_ENABLE_ON|
            SEC_PRESCAL_1_1 |
            PRI_PRESCAL_1_1 ,
            SPI_ENABLE      |
            SPI_IDLE_CON    |
            SPI_RX_OVFLOW   );


}

unsigned short ReadFlashStatus(void)
{

	int nWords  =   1;

	while(nWords--)
	{
		unsigned short  rxData;

		FLS_CE_LAT = 0;
                
                putcSPI1(RDSR);	// send data
                rxData = getcSPI1();
                putcSPI1(0x00);
		rxData = getcSPI1();		// retreive the received data
                                
                FLS_CE_LAT = 1;
                return rxData;
	}

}


unsigned short UnprotectFlash (void)
{
    unsigned short  rxData;
    
    // Enable Write Status Register
    FLS_WP_LAT=1;			// Disable write protection
    FLS_CE_LAT=0;			// Chip Enable
    putcSPI1(EWSR);
    rxData = getcSPI1();           //read dummy value
    FLS_CE_LAT=1;			// Chip Disable


    // WSR
    FLS_CE_LAT=0;			// Chip Enable
    putcSPI1(WRSR);		// Send WRSR command via SPI
    rxData = getcSPI1();           //read dummy value


    // Unlock BPL
    putcSPI1(0b00011100);        // Send 'Unlock BPL' code via SPI
    rxData = getcSPI1();           //read dummy value
    
    FLS_CE_LAT=1;			// Chip Disable
    FLS_WP_LAT=0;			// Enable write protection


    // Enable Write Status Register
    FLS_WP_LAT=1;			// Disable write protection
    FLS_CE_LAT=0;			// Chip Enable
    SpiChnPutC(1, EWSR);
    rxData = getcSPI1();           //read dummy value
    FLS_CE_LAT=1;			// Chip Disable
    Nop();


    // WSR
    FLS_CE_LAT=0;			// Chip Enable
    SpiChnPutC(1, WRSR);
    rxData = getcSPI1();           //read dummy value


    // Unprotect All
    SpiChnPutC(1, 0b10000000);          // Send 'Unprotect All' code via SPI
    rxData = getcSPI1();           // read dummy value
    FLS_CE_LAT=1;			// Chip Disable
    FLS_WP_LAT=0;			// Enable write protection

    rxData = ReadFlashStatus();
    
}

unsigned short ReadFlash (unsigned int address)
{
    unsigned int *p_add;    //address pointer
    
    p_add = &address; //pointer of the address to write takes value of the address where the value is stored

    unsigned int    i;
    unsigned char   rxData = 0x00; //recived data
    unsigned char   add_hi  =   address >> 16,
                    add_med =   address >> 8,
                    add_low =   address;

    FLS_CE_LAT = 0;
    
    putcSPI1(READ);
    rxData = getcSPI1();
    putcSPI1(add_hi);
    rxData = getcSPI1();
    putcSPI1(add_med);
    rxData = getcSPI1();
    putcSPI1(add_low);
    rxData = getcSPI1();

    for(i=0; i<4; i++)
    {
        putcSPI1(0x00);
        rxData = getcSPI1();
    }
    FLS_CE_LAT = 1;

    return rxData;
}

void           ReadFlashHS (unsigned int    address ,
                            unsigned char   pack_size,
                            unsigned char   ret_array[])
{
    unsigned char   i = 0; //general counter
    unsigned char   rxData = 0x00; //recived data
    unsigned char   add_hi  =   address >> 16,
                    add_med =   address >> 8,
                    add_low =   address;

    FLS_CE_LAT = 0;

    putcSPI1(RDHS);
    rxData = getcSPI1();
    putcSPI1(add_hi);
    rxData = getcSPI1();
    putcSPI1(add_med);
    rxData = getcSPI1();
    putcSPI1(add_low);
    rxData = getcSPI1();
    putcSPI1(0x00); //write dummy cicle
    rxData = getcSPI1();

    while (i != pack_size)
    {
        putcSPI1(0x00);
        ret_array[i] = getcSPI1();
        i++;
    }
    


    FLS_CE_LAT = 1;

    return rxData;
}

void WriteFlash     (unsigned int   address,
                    unsigned char   pack_size,
                    unsigned char   data[])
{

    unsigned char   rxData      =   0x00;
    unsigned char   i           =   0;

    while(i != pack_size)
    {
        FLS_CE_LAT = 0;
        putcSPI1(WREN);
        rxData = getcSPI1();

        FLS_CE_LAT = 1;


        FLS_CE_LAT = 0;
        putcSPI1(WRBY);
        rxData = getcSPI1();
        putcSPI1(address >> 16);
        rxData = getcSPI1();
        putcSPI1(address >> 8);
        rxData = getcSPI1();
        putcSPI1(address);
        rxData = getcSPI1();
        putcSPI1(data[i]);
        rxData = getcSPI1();

        

        FLS_CE_LAT = 1;

        address++;
        i++;
        delay_ms(1);
    }

}

void WriteFlashAAI  (unsigned int   address , //initial address to write at
                    unsigned char   pack_size,
                    unsigned char   data[]    //data array to write
                    )
{
    unsigned char   i       =   0;
    unsigned int    rxData  =   0x00;
    unsigned char   add2    =   (address >> 16),
                    add1    =   (address >> 8),
                    add0    =   address;
    

    //initialize flash AAI

    FLS_CE_LAT = 0;
    putcSPI1(EBSY);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

    FLS_CE_LAT = 0;
    putcSPI1(WREN);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

    FLS_CE_LAT = 0;
    putcSPI1(AAIP);
    rxData = getcSPI1();
    putcSPI1(add2);
    rxData = getcSPI1();
    putcSPI1(add1);
    rxData = getcSPI1();
    putcSPI1(add0);
    rxData = getcSPI1();
    putcSPI1(data[0]);
    rxData = getcSPI1();
    putcSPI1(data[1]);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

    //recurrent write

    rxData = ReadFlashStatus();

    //while (ReadFlashStatus != 0x80); //TODO value not right, update

    i = 2; //value is 2 because 2 data bytes are already sent
    while (i != pack_size)
    {
        FLS_CE_LAT = 0;
        putcSPI1(AAIP);
        rxData = getcSPI1();
        putcSPI1(data[i]);
        rxData = getcSPI1();
        i++;
        putcSPI1(data[i]);
        rxData = getcSPI1();
        i++;
        FLS_CE_LAT = 1;

        //while (ReadFlashStatus != 0x80);
    }

    //disable flash AAI

    FLS_CE_LAT = 0;
    putcSPI1(WRDI);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

    FLS_CE_LAT = 0;
    putcSPI1(DBSY);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

}

unsigned short ReadFlashAAI (void)
{

    unsigned short  rxData1 =   0xAB,
                    rxData2 =   0xCD,
                    rxData  =   0x0000;
    
    FLS_CE_LAT = 0;
    
    putcSPI1(0x90);
    rxData = getcSPI1();
    putcSPI1(0x00);
    rxData = getcSPI1();
    putcSPI1(0x00);
    rxData = getcSPI1();
    putcSPI1(0x01);
    rxData = getcSPI1();
    putcSPI1(0x00);
    rxData1 = getcSPI1();
    putcSPI1(0x00);
    rxData2 = getcSPI1();
    FLS_CE_LAT = 1;
    rxData = rxData2 | (rxData1 << 8);

    return rxData;
}

unsigned int ReadFlashJedecID (void)
{

    unsigned int    rxData1 =   0xAB,
                    rxData2 =   0xCD,
                    rxData3 =   0xEF,
                    rxData  =   0x00000000;

    FLS_CE_LAT = 0;

    putcSPI1(0x9F);
    rxData = getcSPI1();
    putcSPI1(0x00);
    rxData1 = getcSPI1();
    putcSPI1(0x01);
    rxData2 = getcSPI1();
    putcSPI1(0x02);
    rxData3 = getcSPI1();
    FLS_CE_LAT = 1;
    rxData = rxData3 | (rxData2 << 8) | (rxData1 << 16);

    return rxData;
}

void EraseFlash(void)
{
    unsigned char   rxData = 0x00;

    FLS_CE_LAT = 0;
    putcSPI1(WREN);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;


    FLS_CE_LAT = 0;
    putcSPI1(ERCP);
    rxData = getcSPI1();
    FLS_CE_LAT = 1;

    delay_ms(50); //wait 50ms for chip to erase
}


//delays
void delay_ms(unsigned int delay)
{
    unsigned int int_status;
    while( delay-- )
    {
     int_status = INTDisableInterrupts();
     OpenCoreTimer(SYS_FREQ / 2000);    //TODO add a milisecond general parameter
     INTRestoreInterrupts(int_status);
     mCTClearIntFlag();
     while( !mCTGetIntFlag() );
    }
    mCTClearIntFlag();
}

